package com.example.easycharge.service;

import com.alibaba.fastjson.JSONObject;
import com.example.easycharge.entity.*;
import com.example.easycharge.notRepEntity.*;
import com.example.easycharge.repository.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

@Slf4j
@Service
public class ChargeService {
    // 当前排到的号，快慢充采用统一的顺序号
    private int nowOrderNumber = 0;

    private int MAX_QUEUE_LENGTH = 2;
    private int MAX_AREA_SIZE = 10;

    private final HashMap<Long, Long> pileToQueue = new HashMap<>();
    private final HashMap<Long, WaitQueue> waitQueue = new HashMap<>();
    private final WaitArea waitArea = new WaitArea();

    @Autowired
    private VehicleRepository vehicleRepository;
    @Autowired
    private ChargePileRepository chargePileRepository;
    @Autowired
    private BillService billService;


    public JSONObject beforeRequest(long customerId, ChargeRequest chargeRequest) {
        JSONObject returnJson = new JSONObject();
        long vehicleId = chargeRequest.getVehicleId();

        if (waitQueue.size() == 0) {
            returnJson.put("error", "没有足够充电桩");
            return returnJson;
        }

        if (!waitArea.inArea(chargeRequest)) {
            returnJson.put("error", "车辆不在允许坐标范围");
            return returnJson;
        }
        Vehicle vehicle = vehicleRepository.findVehicleByVehicleId(chargeRequest.getVehicleId());
        chargeRequest.setTotalEnergy(vehicle.getTotalEnergy());
        chargeRequest.setCustomerId(customerId);
        chargeRequest.setBatteryPercent(chargeRequest.getLeftEnergy() / chargeRequest.getTotalEnergy());
        // 设定申请充电量
        double emptyEnergy = chargeRequest.getTotalEnergy() - chargeRequest.getLeftEnergy();
        chargeRequest.setRequestEnergy(Math.min(chargeRequest.getRequestEnergy(), emptyEnergy));
        int orderNum = getOrder();
        chargeRequest.setOrderNumber(orderNum);

        returnJson = receiveRequest(chargeRequest);

        if (returnJson.containsKey("error")) {
            return returnJson;
        }
        // 将车辆设置为已充电
        vehicleRepository.setVehicleState(true, vehicleId);

        returnJson.put("result", "申请充电成功");
        returnJson.put("orderNum", orderNum);
        return returnJson;
    }

    private JSONObject receiveRequest(ChargeRequest chargeRequest) {
        JSONObject jsonObject = new JSONObject();
        WaitQueue shortestQueue = findShortestQueue(chargeRequest.isFastCharge());

        if (shortestQueue == null) {
            if (waitArea.getAreaSize() < MAX_AREA_SIZE) {
                waitArea.addNewRequestToArea(chargeRequest);
                jsonObject.put("message", "无空余充电队列，已将您添加至等候区中");
            } else {
                jsonObject.put("error", "等待区已无空位，申请充电失败");
            }
        } else {
            // 修改充电请求中的选择充电桩为最短充电桩
            chargeRequest.setSelectPileId(shortestQueue.getPileID());
            addVehicleToPile(chargeRequest);
            jsonObject.put("message", "存在空余充电队列，已将您添加至此队列中");
            jsonObject.put("detail", chargeRequest);
        }
        return jsonObject;
    }

    public void addNewQueue(ChargePile pile, long queueId) {
        long pileId = pile.getChargePileId();
        WaitQueue newQueue = new WaitQueue(pileId, pile.getChargePower(), pile.isFast());
        waitQueue.put(queueId, newQueue);
        pileToQueue.put(pileId, queueId);
    }

    public void addVehicleToPile(ChargeRequest chargeRequest) {
        long pileId = chargeRequest.getSelectPileId();
        ChargePile chargePile = chargePileRepository.findChargePileByChargePileId(pileId);

        chargeRequest.setChargePower(chargePile.getChargePower());
        long queueId = pileToQueue.get(pileId);
        WaitQueue queue = waitQueue.get(queueId);
        // 设定预计充电所需时间
        int chargeTime = (int) (chargeRequest.getRequestEnergy() / chargePile.getChargePower() * 60);
        chargeRequest.setEstimateChargeTime(chargeTime);
        // 设定预计等待时间
        int waitTime = queue.getTotalWaitTime();
        chargeRequest.setEstimateWaitTime(waitTime);
        queue.addNewVehicle(chargeRequest, chargeTime);
        // 开始记账
        billService.startBill(chargeRequest.getVehicleId(), pileId, chargeRequest.getChargePower());
    }

    public void removeQueue(long pileId) {
        waitQueue.remove(pileToQueue.get(pileId));
        pileToQueue.remove(pileId);
    }

    public void transVehicle(long pileId) {
        long queueId = pileToQueue.get(pileId);
        WaitQueue removeQueue = waitQueue.get(queueId);
        // 与其他充电桩等待车辆合为一组，重新安排调度
        LinkedList<ChargeRequest> scheduleList = new LinkedList<>();

        for (WaitQueue waitQueue : waitQueue.values()) {
            // 只把同一类型的充电桩加入进来
            if (waitQueue != removeQueue && (waitQueue.isFast()) == removeQueue.isFast()) {
                scheduleList.addAll(waitQueue.removeWaitVehicle());
            }
        }
        // 按照时间顺序排序
        scheduleList.sort(Comparator.comparingInt(ChargeRequest::getOrderNumber));
        int leftSpace = (waitQueue.size() - 1) * MAX_QUEUE_LENGTH;
        while (scheduleList.size() > 0 && leftSpace > 0) {
            for (WaitQueue targetQueue : waitQueue.values()) {
                if (targetQueue.isFast() != removeQueue.isFast()) {
                    continue;
                }
                ChargeRequest movedRequest = scheduleList.pollFirst();
                movedRequest.setSelectPileId(targetQueue.getPileID());
                addVehicleToPile(movedRequest);
                leftSpace--;
                if (scheduleList.size() == 0 || leftSpace == 0) {
                    break;
                }
            }
        }
        // 把多余的车辆都给移到等候区中
        while (scheduleList.size() > 0) {
            ChargeRequest tailRequest = scheduleList.pollLast();
            waitArea.insertRequest(tailRequest);
        }
        // 清除原先队列中的车辆
        removeQueue.getChargeRequestLinkedList().clear();
    }

    public JSONObject cancelCharge(long vehicleId) {
        // 先判断是否存在于等候区中
        JSONObject jsonObject = new JSONObject();
        if (waitArea.cancelCharge(vehicleId)) {
            // 将车辆设定为未充电
            vehicleRepository.setVehicleState(false, vehicleId);
            // 如果成功移除说明在等候区中
            jsonObject.put("message", "等候区的车辆已取消充电");
            return jsonObject;
        }

        long pileId = getPileIdByVehicleId(vehicleId);
        WaitQueue thisQueue = waitQueue.get(pileToQueue.get(pileId));
        QueueStateEnum queueStateEnum = thisQueue.getState();
        if (thisQueue.getChargingVehicle().getVehicleId() == vehicleId) {
            // 如果在队首
            if (queueStateEnum == QueueStateEnum.ILLEGAL_OCCUPATION) {
                //如果是非法占用
                thisQueue.cancelChargeTime();
                Bill endBill = billService.endCharge(vehicleId);
                jsonObject.put("bill", endBill);
                if (thisQueue.getWaitLength() != 0) {
                    thisQueue.nextVehicle();
                }
                // 从等候区获取一辆车
                nextRequest(thisQueue.isFast(), pileId);
                // 将车辆设置为未在充电
                vehicleRepository.setVehicleState(false, vehicleId);
            } else if (queueStateEnum == QueueStateEnum.WAITING_DRIVE_IN) {
                //如果是等待驶入的话，直接结束即可

                // 更新队列状态
                thisQueue.cancelChargeState();
                // 更新等待队列中车辆的等待时间
                thisQueue.cancelChargeTime();

                Bill endBill = billService.endCharge(vehicleId);
                jsonObject.put("bill", endBill);

//                if (thisQueue.getWaitLength() != 0) {
                thisQueue.nextVehicle();
//            }
                // 从等候区获取一辆车
                nextRequest(thisQueue.isFast(), pileId);
                // 将车辆设置为未在充电
                vehicleRepository.setVehicleState(false, vehicleId);
            } else if (queueStateEnum == QueueStateEnum.WAITING_PLUG_IN || queueStateEnum == QueueStateEnum.CHARGING) {
                // 车辆的状态为等待插入 或 正在充电
                // 这种状态车位上是有车的，没法让下辆车进入
                thisQueue.cancelChargeState();
                thisQueue.cancelChargeTime();
                Bill endBill = billService.endCharge(vehicleId);
                jsonObject.put("bill", endBill);
            }
            jsonObject.put("message", "车辆移除成功");
        } else {
            // 不在队头
            boolean hasRemoved = thisQueue.removeVehicle(vehicleId);
            if (hasRemoved) {
                // 从等候区获取一辆车
                nextRequest(thisQueue.isFast(), pileId);
                jsonObject.put("message", "取消充电，车辆已移除");
                vehicleRepository.setVehicleState(false, vehicleId);
            } else {
                jsonObject.put("message", "车辆不存在等候区和充电区中");
            }
        }
        return jsonObject;
    }

    public JSONObject plugInPile(long vehicleId) {
        JSONObject jsonObject = new JSONObject();
        long pileId = getPileIdByVehicleId(vehicleId);
        if (pileId == 0) {
            jsonObject.put("message", "车辆不存在等候区和充电区中");
            return jsonObject;
        }
        WaitQueue thisQueue = waitQueue.get(pileToQueue.get(pileId));
        thisQueue.startCharge();
        // 开始计费
        billService.startCharge(vehicleId);
        jsonObject.put("message", "插入成功，开始充电");
        return jsonObject;
    }

    public JSONObject unplugPile(long vehicleId) {
        // 拔出充电桩
        JSONObject jsonObject = new JSONObject();
        long pileId = getPileIdByVehicleId(vehicleId);
        WaitQueue thisQueue = waitQueue.get(pileToQueue.get(pileId));
        // 队列的状态为等待拔出
        if (thisQueue.getState() == QueueStateEnum.WAITING_PLUG_OFF) {
            jsonObject.put("message", "车辆充电头已成功拔出");
        } else if (thisQueue.getState() == QueueStateEnum.CHARGING) {
            jsonObject.put("message", "车辆正在充电中，充电头被强行拔出");
        }
        billService.endCharge(vehicleId);
        thisQueue.unplug();
        return jsonObject;
    }

    /*
        因为没有实际的充电桩发送驶离信息
        因此用接口来模拟车辆的驶入和驶离
        实际部署时应该是由充电桩或者其他红外识别设备来识别车辆是否驶离
     */
    public JSONObject driveIn(long vehicleId) {
        JSONObject jsonObject = new JSONObject();
        long pileId = getPileIdByVehicleId(vehicleId);
        if (pileId == 0) {
            jsonObject.put("message", "车辆不存在等候区和充电区中");
            return jsonObject;
        }

        long queueId = pileToQueue.get(pileId);

        WaitQueue thisQueue = waitQueue.get(queueId);
        if (thisQueue.getState() == QueueStateEnum.FREE) {
            thisQueue.driveIn(vehicleId);
            jsonObject.put("message", "空闲车位被占据");
        } else if (thisQueue.getState() == QueueStateEnum.WAITING_DRIVE_IN) {
            thisQueue.driveIn(vehicleId);
            if (thisQueue.getChargingVehicle().getVehicleId() == vehicleId) {
                jsonObject.put("message", "成功驶入");
            } else {
                jsonObject.put("message", "预约车位被非法驶入");
            }
        }
        return jsonObject;
    }

    public JSONObject driveAway(long vehicleId) {
        // 驶离停车位
        JSONObject jsonObject = new JSONObject();
        long pileId = getPileIdByVehicleId(vehicleId);
        if (pileId == 0) {
            jsonObject.put("message", "车辆不存在等候区和充电区中");
            return jsonObject;
        }
        WaitQueue thisQueue = waitQueue.get(pileToQueue.get(pileId));
        if (thisQueue.getChargingVehicle() == null) {
            return jsonObject;
        }
        if (thisQueue.getState() == QueueStateEnum.ILLEGAL_OCCUPATION) {
            // 非法车辆驶离
            thisQueue.driveAway();
        } else {
            if (thisQueue.getState() == QueueStateEnum.WAITING_PLUG_IN) {
                // 等待插入的时候车辆直接开走，我们认为是取消充电了
                thisQueue.cancelChargeState();
            } else if (thisQueue.getState() == QueueStateEnum.WAITING_DRIVE_AWAY) {
                thisQueue.driveAway();
                if (thisQueue.getWaitLength() != 0) {
                    thisQueue.nextVehicle();
                }
            }
            // 从等候区中拉个新车来到队列里
            nextRequest(thisQueue.isFast(), pileId);
            // 账单记录
            Bill thisBill = billService.endBill(vehicleId);
            // 将车辆设置为未在充电
            vehicleRepository.setVehicleState(false, vehicleId);
            // 给充电桩增加充电次数
            chargePileRepository.addWorkTimes(pileId);
            // 给充电桩增加充电总时长(充电费+服务费)
            chargePileRepository.addBill(pileId, thisBill.getChargeBill(), thisBill.getServiceBill());
            // 增加总充电量
            double addQuantity = (double) thisBill.getChargeMinutes() * thisBill.getChargePower() / 60;
            chargePileRepository.addChargeQuantity(pileId, addQuantity);
        }
        jsonObject.put("message", "车辆已驶离，祝您一路顺风");
        return jsonObject;
    }

    // 做一个复用，从等候区获取车辆调用这个方法
    // 注意，是从等候区取车辆到等候队列中，不是直接去充电
    // 应该是在等候队列有空闲的时候调用（比如有车辆开始充电了或者取消了）
    private void nextRequest(boolean isFast, long pileId) {
        ChargeRequest nextRequest = waitArea.getNextRequest(isFast);
        if (nextRequest != null) {
            nextRequest.setSelectPileId(pileId);
            addVehicleToPile(nextRequest);
        }
    }

    public List<JSONObject> getAllVehicleInfo(long customerId) {

        List<JSONObject> resultList = new LinkedList<>();
        List<Long> vehicleIdList = vehicleRepository.getChargingVehicle(customerId);
        // 遍历每一个队列
        for (Long vehicleId : vehicleIdList) {
            resultList.add(getSigVehicleInfo(vehicleId));
        }
        return resultList;
    }

    public JSONObject getSigVehicleInfo(long vehicleId) {
        ChargeRequest areaRequest = waitArea.getRequestByVehicleId(vehicleId);
        if (areaRequest != null) {
            // 此车在等候区中，直接返回即可
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("vehicleId", vehicleId);
            jsonObject.put("chargeDetail", areaRequest);
            jsonObject.put("orderNum", areaRequest.getOrderNumber());
            jsonObject.put("vehicleState", "waitingArea");
            jsonObject.put("waitAreaLength", waitArea.countAhead(vehicleId));
            return jsonObject;
        }

        for (WaitQueue waitQueue : waitQueue.values()) {
            if (waitQueue.getChargingVehicle() != null && waitQueue.getChargingVehicle().getVehicleId() == vehicleId) {
                JSONObject jsonObject = new JSONObject();
                ChargeRequest chargeRequest = waitQueue.getChargingVehicle();
                jsonObject.put("orderNum", chargeRequest.getOrderNumber());
                jsonObject.put("chargeDetail", chargeRequest);
                jsonObject.put("vehicleState", waitQueue.getState());
                return jsonObject;
            }
            if (waitQueue.getWaitLength() != 0) {
                LinkedList<ChargeRequest> waitList = waitQueue.getChargeRequestLinkedList();
                int waitTime = waitQueue.getChargingVehicle().getEstimateChargeTime();
                // 遍历队列里的每一个请求
                int queueIndex = 1;
                for (ChargeRequest chargeRequest : waitList) {
                    chargeRequest.setEstimateWaitTime(waitTime);
                    if (chargeRequest.getVehicleId() == vehicleId) {
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put("orderNum", chargeRequest.getOrderNumber());
                        jsonObject.put("chargeDetail", chargeRequest);
                        jsonObject.put("waitQueueLength", queueIndex);
                        jsonObject.put("vehicleState", "waiting");
                        return jsonObject;
                    }
                    // 每次都计算一下等待时间
                    waitTime += chargeRequest.getEstimateChargeTime();
                    queueIndex++;
                }
            }
        }
        return null;
    }

    public HashMap<Long, WaitQueue> getWaitQueue() {
        return waitQueue;
    }

    public List<WaitQueue> getWaitQueueList() {
        List<WaitQueue> result = new ArrayList<>(waitQueue.values());
        result.sort(Comparator.comparingInt(o -> (int) o.getPileID()));
        return result;
    }

    public int getOrder() {
        nowOrderNumber++;
        return nowOrderNumber;
    }

    // 恢复队列，合为一组重新调度
    public void recoveryQueue(long pileId) {
        WaitQueue recoveryQueue = waitQueue.get(pileToQueue.get(pileId));
        boolean isFast = recoveryQueue.isFast();
        LinkedList<ChargeRequest> recoveryList = new LinkedList<>();
        for (WaitQueue waitQueue : waitQueue.values()) {
            if (waitQueue.isFast() == isFast) {
                recoveryList.addAll(waitQueue.removeWaitVehicle());
            }
        }
        // 按时间进行排序
        recoveryList.sort(Comparator.comparingInt(ChargeRequest::getOrderNumber));
        while (recoveryList.size() > 0) {
            for (WaitQueue target : waitQueue.values()) {
                if (target.isFast() == isFast) {
                    ChargeRequest reSortRequest = recoveryList.pollFirst();
                    reSortRequest.setSelectPileId(target.getPileID());
                    addVehicleToPile(reSortRequest);
                }
            }
        }
    }

    // 找出最短的那个队列，所有队列都没有位置的话就返回空
    private WaitQueue findShortestQueue(boolean fast) {
        WaitQueue result = null;
        for (WaitQueue waitQueue : waitQueue.values()) {
            if (!waitQueue.isFull() && waitQueue.isFast() == fast) {
                if (result == null || waitQueue.getTotalWaitTime() < result.getTotalWaitTime()) {
                    result = waitQueue;
                }
            }
        }
        return result;
    }

    // 更改充电电量
    public JSONObject resetCapacity(long vehicleId, NewCapacity newCapacity) {
        JSONObject result = new JSONObject();
        ChargeRequest chargeRequest = waitArea.getRequestByVehicleId(vehicleId);
        if (chargeRequest == null) {
            result.put("message", "修改充电量失败，请检查车辆是否已抵达充电区");
            return result;
        }
        chargeRequest.changeCapacity(newCapacity);
        result.put("message", "修改充电量成功");
        return result;
    }

    // 更改充电模式
    public JSONObject resetPatten(long vehicleId, boolean isFast) {
        JSONObject jsonObject = new JSONObject();
        ChargeRequest chargeRequest = waitArea.getRequestByVehicleId(vehicleId);
        if (chargeRequest == null) {
            jsonObject.put("message", "修改充电模式失败，请检查车辆是否已抵达充电区");
            return jsonObject;
        }
        chargeRequest.setFastCharge(isFast);
        chargeRequest.setOrderNumber(getOrder());
        waitArea.moveToTail(vehicleId, isFast);
        jsonObject.put("message", "修改充电模式成功");
        return jsonObject;
    }

    private ChargeRequest getRequestByVehicleId(long vehicleId) {
        for (WaitQueue queue : waitQueue.values()) {
            ChargeRequest targetRequest = queue.getRequestById(vehicleId);
            if (targetRequest != null) {
                return targetRequest;
            }
        }
        return null;
    }

    private long getPileIdByVehicleId(long vehicleId) {
        ChargeRequest request = getRequestByVehicleId(vehicleId);
        long pileId = 0;
        if (request != null) {
            pileId = request.getSelectPileId();
        }
        return pileId;
    }

    public JSONObject initQueue() {
        waitQueue.clear();
        waitArea.clear();
        JSONObject result = new JSONObject();
        result.put("Queue", "已完成队列初始化");
        return result;
    }

    public JSONObject initVehicle() {
        vehicleRepository.initQueue();
        JSONObject result = new JSONObject();
        result.put("Vehicle", "已完成车辆初始化");
        result.put("VehicleDetail", vehicleRepository.findAll());
        return result;
    }

}
